package com.kilman.logger.log;

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.commons.lang.StringUtils;

import com.kilman.logger.log.ActiveLog.ActiveLogState;
import com.kilman.logger.log.control.LogLine;
import com.kilman.logger.log.control.LogLines;
import com.kilman.logger.log.control.LogLinesController;
import com.kilman.logger.log.control.ManagedLog;
import com.kilman.logger.provider.ManagedLogsProvider;

public class ActiveLogs {

    private Map<ManagedLog, ActiveLog> activeLogs;
    
    private ActiveLogs() { 
        activeLogs = new ConcurrentHashMap<ManagedLog, ActiveLog>();
    }
    
    public boolean initActiveLog(ManagedLog managedLog) {
 
        if (activeLogs.containsKey(managedLog)) {
            return false;
        }
        
        String path = managedLog.getPath();
        String host = managedLog.getHost();
        String username = managedLog.getUsername();
        String password = managedLog.getPassword();
        LogLinesController logLinesController = managedLog.getLogLinesController();
        
        ActiveLog activeLog = new ActiveLog(path, host, username, password, 
                logLinesController);
        
        activeLogs.put(managedLog, activeLog);
        return true;
    }
    
    public boolean startActiveLog(ManagedLog managedLog) {

        if (!activeLogs.containsKey(managedLog)) {
            return false;
        }

        ActiveLog activeLog = activeLogs.get(managedLog);
        
        if (activeLog.getState() != ActiveLogState.NEW) {
            return false;
        }
        
        activeLog.start();
        return true;
        
    }
    
    public synchronized boolean stopActiveLog(ManagedLog managedLog) {
        
        if (!activeLogs.containsKey(managedLog)) {
            return false;
        }

        ActiveLog activeLog = activeLogs.get(managedLog);
        
        if (activeLog.getState() != ActiveLogState.RUNNING) {
            return false;
        }
        
        activeLog.stop();
        
        activeLogs.remove(managedLog);
        
        return true;
    }
    
    public synchronized void stopAllActiveLogs() {
        for (ManagedLog managedLog : activeLogs.keySet()) {
            stopActiveLog(managedLog);
        }
    }
    
    /* 
     * This method is relevant only when all managed logs are on the same machine, otherwise
     * host, username and password are meaningless
     */
    public synchronized void killAllActiveLogs(ManagedLogsProvider provider) {
        stopAllActiveLogs();
        RawActiveLog.killAllRawActiveLogsByUsername(
                provider.getHost(), provider.getUsername(), provider.getPassword());
    }
    
    public LogLines getActiveLogLines(ManagedLog managedLog, int fromLine, boolean update, 
            String[] includeRegex, String[] excludeRegex) {
        
        if (!activeLogs.containsKey(managedLog)) {
            return null;
        }

        ActiveLog activeLog = activeLogs.get(managedLog);
        
        LinkedList<LogLine> result = new LinkedList<LogLine>();
        synchronized(this) {
            
            if (update && activeLog.getState() == ActiveLogState.RUNNING) {
                activeLog.updateLines();
            }

            List<LogLine> original = activeLog.getLines();
            
            if (fromLine < 0) {
                fromLine = 0;
            }
            
            for (int i=fromLine; i<original.size(); i++) {
                result.add(original.get(i));
            }
        
        }
        
        int preFilterSize = result.size();
        
        if (result.size() > 0 && includeRegex != null) {
            
            String regex = StringUtils.join(includeRegex, "|");
            
            Pattern pattern;
            try {
                pattern = Pattern.compile(regex);
                ListIterator<LogLine> iterator = result.listIterator();
                
                while (iterator.hasNext()) {
                    LogLine line = iterator.next();
                    Matcher matcher = pattern.matcher(line.getRawLine());
                    if (!matcher.find()) {
                        iterator.remove();
                    }
                }
                
            } catch (PatternSyntaxException e) {
                /* bad regex, do nothing */
            }
        }
        
        if (result.size() > 0 && excludeRegex != null) {
            for (String regex : excludeRegex) {
                Pattern pattern;
                try {
                    pattern = Pattern.compile(regex);
                } catch (PatternSyntaxException e) {
                    continue;
                }
                
                ListIterator<LogLine> iterator = result.listIterator();
                
                while (iterator.hasNext()) {
                    LogLine line = iterator.next();
                    Matcher matcher = pattern.matcher(line.getRawLine());
                    if (matcher.find()) {
                        iterator.remove();
                    }
                }
                    
            }
        }
        
        return new LogLines(result,preFilterSize);
        
    }
    
}
